using Nop.Core.Domain.Directory;
using Nop.Core.Domain.XJ;
using Nop.Services.Directory;
using Nop.Services.XJ;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Nop.Services.Catalog
{
	/// <summary>
	/// Extensions
	/// </summary>
	public static class CategoryExtensions
	{
		/// <summary>
		/// Sort categories for tree representation
		/// </summary>
		/// <param name="source">Source</param>
		/// <param name="parentId">Parent category identifier</param>
		/// <param name="ignoreCategoriesWithoutExistingParent">A value indicating whether categories without parent category in provided category list (source) should be ignored</param>
		/// <returns>Sorted categories</returns>
		public static IList<XJEquipmentCategory> SortCategoriesForTree(this IList<XJEquipmentCategory> source, int parentId = 0, bool ignoreCategoriesWithoutExistingParent = false)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			var result = new List<XJEquipmentCategory>();

			foreach (var cat in source.Where(c => c.PId == parentId).ToList())
			{
				result.Add(cat);
				result.AddRange(SortCategoriesForTree(source, cat.Id, true));
			}
			if (!ignoreCategoriesWithoutExistingParent && result.Count != source.Count)
			{
				//find categories without parent in provided category source and insert them into result
				foreach (var cat in source)
					if (result.FirstOrDefault(x => x.Id == cat.Id) == null)
						result.Add(cat);
			}
			return result;
		}

		public static IList<XJEquipmentTemp> SortEquipmentsForTree(this IList<XJEquipmentTemp> source, int parentId = 0, bool ignoreCategoriesWithoutExistingParent = false)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			var result = new List<XJEquipmentTemp>();

			foreach (var cat in source.Where(c => c.PId == parentId).ToList())
			{
				result.Add(cat);
				result.AddRange(SortEquipmentsForTree(source, cat.Id, true));
			}
			if (!ignoreCategoriesWithoutExistingParent && result.Count != source.Count)
			{
				//find categories without parent in provided category source and insert them into result
				foreach (var cat in source)
					if (result.FirstOrDefault(x => x.Id == cat.Id) == null)
						result.Add(cat);
			}
			return result;
		}

		public static IList<XJEquipment4StockIn> SortEquipmentsForTree(this IList<XJEquipment4StockIn> source, int parentId = 0, bool ignoreCategoriesWithoutExistingParent = false)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			var result = new List<XJEquipment4StockIn>();

			foreach (var cat in source.Where(c => c.PId == parentId).ToList())
			{
				result.Add(cat);
				result.AddRange(SortEquipmentsForTree(source, cat.Id, true));
			}
			if (!ignoreCategoriesWithoutExistingParent && result.Count != source.Count)
			{
				//find categories without parent in provided category source and insert them into result
				foreach (var cat in source)
					if (result.FirstOrDefault(x => x.Id == cat.Id) == null)
						result.Add(cat);
			}
			return result;
		}

		public static IList<XJEquipment4StockOut> SortEquipmentsForTree(this IList<XJEquipment4StockOut> source, int parentId = 0, bool ignoreCategoriesWithoutExistingParent = false)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			var result = new List<XJEquipment4StockOut>();

			foreach (var cat in source.Where(c => c.PId == parentId).ToList())
			{
				result.Add(cat);
				result.AddRange(SortEquipmentsForTree(source, cat.Id, true));
			}
			if (!ignoreCategoriesWithoutExistingParent && result.Count != source.Count)
			{
				//find categories without parent in provided category source and insert them into result
				foreach (var cat in source)
					if (result.FirstOrDefault(x => x.Id == cat.Id) == null)
						result.Add(cat);
			}
			return result;
		}

		public static IList<Department> SortDepartmentsForTree(this IList<Department> source, int parentId = 0, bool ignoreCategoriesWithoutExistingParent = false)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			var result = new List<Department>();

			foreach (var cat in source.Where(c => c.PId == parentId).ToList())
			{
				result.Add(cat);
				result.AddRange(SortDepartmentsForTree(source, cat.Id, true));
			}
			if (!ignoreCategoriesWithoutExistingParent && result.Count != source.Count)
			{
				//find categories without parent in provided category source and insert them into result
				foreach (var cat in source)
					if (result.FirstOrDefault(x => x.Id == cat.Id) == null)
						result.Add(cat);
			}
			return result;
		}

		/// <summary>
		/// Get formatted category breadcrumb 
		/// Note: ACL and store mapping is ignored
		/// </summary>
		/// <param name="category">XJEquipmentCategory</param>
		/// <param name="categoryService">XJEquipmentCategory service</param>
		/// <param name="separator">Separator</param>
		/// <param name="languageId">Language identifier for localization</param>
		/// <returns>Formatted breadcrumb</returns>
		public static string GetFormattedBreadCrumb(this XJEquipmentCategory category,
			IXJEquipmentCategoryService categoryService,
			string separator = ">>")
		{
			string result = string.Empty;

			var breadcrumb = GetCategoryBreadCrumb(category, categoryService, true);
			for (int i = 0; i <= breadcrumb.Count - 1; i++)
			{
				var categoryName = breadcrumb[i].Name;
				result = String.IsNullOrEmpty(result)
					? categoryName
					: string.Format("{0} {1} {2}", result, separator, categoryName);
			}

			return result;
		}
		public static string GetFormattedBreadCrumb(this XJEquipmentTemp equipment,
			IXJEquipmentService equipmentSService,
			string separator = ">>")
		{
			string result = string.Empty;

			var breadcrumb = GetEquipmentBreadCrumb(equipment, equipmentSService, true);
			for (int i = 0; i <= breadcrumb.Count - 1; i++)
			{
				var equipmentName = breadcrumb[i].Name;
				result = String.IsNullOrEmpty(result)
					? equipmentName
					: string.Format("{0} {1} {2}", result, separator, equipmentName);
			}

			return result;
		}
		public static string GetFormattedBreadCrumb(this XJEquipment4StockIn equipment,
			IXJEquipmentService equipmentSService,
			string separator = ">>")
		{
			string result = string.Empty;

			var breadcrumb = GetEquipmentBreadCrumb(equipment, equipmentSService);
			for (int i = 0; i <= breadcrumb.Count - 1; i++)
			{
				var equipmentName = breadcrumb[i].Name;
				result = String.IsNullOrEmpty(result)
					? equipmentName
					: string.Format("{0} {1} {2}", result, separator, equipmentName);
			}

			return result;
		}
		public static string GetFormattedBreadCrumb(this XJEquipment4StockOut equipment,
			IXJEquipmentService equipmentSService,
			string separator = ">>")
		{
			string result = string.Empty;

			var breadcrumb = GetEquipmentBreadCrumb(equipment, equipmentSService);
			for (int i = 0; i <= breadcrumb.Count - 1; i++)
			{
				var equipmentName = breadcrumb[i].Name;
				result = String.IsNullOrEmpty(result)
					? equipmentName
					: string.Format("{0} {1} {2}", result, separator, equipmentName);
			}

			return result;
		}
		public static string GetFormattedBreadCrumb(this XJEquipment equipment,
			IXJEquipmentService equipmentSService,
			string separator = ">>")
		{
			string result = string.Empty;

			var breadcrumb = GetEquipmentBreadCrumb(equipment, equipmentSService, true);
			for (int i = 0; i <= breadcrumb.Count - 1; i++)
			{
				var equipmentName = breadcrumb[i].Name;
				result = String.IsNullOrEmpty(result)
					? equipmentName
					: string.Format("{0} {1} {2}", result, separator, equipmentName);
			}

			return result;
		}

		/// <summary>
		/// Get formatted category breadcrumb 
		/// Note: ACL and store mapping is ignored
		/// </summary>
		/// <param name="category">XJEquipmentCategory</param>
		/// <param name="allCategories">All categories</param>
		/// <param name="separator">Separator</param>
		/// <param name="languageId">Language identifier for localization</param>
		/// <returns>Formatted breadcrumb</returns>
		public static string GetFormattedBreadCrumb(this XJEquipmentCategory category,
			IList<XJEquipmentCategory> allCategories,
			string separator = ">>")
		{
			string result = string.Empty;

			var breadcrumb = GetCategoryBreadCrumb(category, allCategories, true);
			for (int i = 0; i <= breadcrumb.Count - 1; i++)
			{
				var categoryName = breadcrumb[i].Name;
				result = String.IsNullOrEmpty(result)
					? categoryName
					: string.Format("{0} {1} {2}", result, separator, categoryName);
			}

			return result;
		}
		public static string GetFormattedBreadCrumb(this Department department,
			IDepartmentService departmentService,
			string separator = ">>")
		{
			string result = string.Empty;

			var breadcrumb = GetDepartmentBreadCrumb(department, departmentService);
			for (int i = 0; i <= breadcrumb.Count - 1; i++)
			{
				var categoryName = breadcrumb[i].Name;
				result = String.IsNullOrEmpty(result)
					? categoryName
					: string.Format("{0} {1} {2}", result, separator, categoryName);
			}

			return result;
		}
		public static IList<Department> GetDepartmentBreadCrumb(this Department category,
			IDepartmentService departmentService)
		{
			if (category == null)
				throw new ArgumentNullException("category");

			var result = new List<Department>();

			//used to prevent circular references
			var alreadyProcessedCategoryIds = new List<int>();

			while (category != null && //not null
				!category.Deleted && //not deleted
				!alreadyProcessedCategoryIds.Contains(category.Id)) //prevent circular references
			{
				result.Add(category);

				alreadyProcessedCategoryIds.Add(category.Id);

				category = departmentService.GetDepartmentById(category.PId);
			}
			result.Reverse();
			return result;
		}

		/// <summary>
		/// Get category breadcrumb 
		/// </summary>
		/// <param name="category">XJEquipmentCategory</param>
		/// <param name="categoryService">XJEquipmentCategory service</param>
		/// <param name="aclService">ACL service</param>
		/// <param name="storeMappingService">Store mapping service</param>
		/// <param name="showHidden">A value indicating whether to load hidden records</param>
		/// <returns>XJEquipmentCategory breadcrumb </returns>
		public static IList<XJEquipmentCategory> GetCategoryBreadCrumb(this XJEquipmentCategory category,
			IXJEquipmentCategoryService categoryService,
			bool showHidden = false)
		{
			if (category == null)
				throw new ArgumentNullException("category");

			var result = new List<XJEquipmentCategory>();

			//used to prevent circular references
			var alreadyProcessedCategoryIds = new List<int>();

			while (category != null && //not null
				!category.Deleted && //not deleted
				(showHidden || category.Published) && //published
				!alreadyProcessedCategoryIds.Contains(category.Id)) //prevent circular references
			{
				result.Add(category);

				alreadyProcessedCategoryIds.Add(category.Id);

				category = categoryService.GetXJEquipmentCategoryById(category.PId);
			}
			result.Reverse();
			return result;
		}

		public static IList<XJEquipmentTemp> GetEquipmentBreadCrumb(this XJEquipmentTemp equipment,
			IXJEquipmentService equipmentService,
			bool showHidden = false)
		{
			if (equipment == null)
				throw new ArgumentNullException("equipment");

			var result = new List<XJEquipmentTemp>();

			//used to prevent circular references
			var alreadyProcessedEquipmentIds = new List<int>();

			while (equipment != null && //not null
				(showHidden || equipment.Published) && //published
				!alreadyProcessedEquipmentIds.Contains(equipment.Id)) //prevent circular references
			{
				result.Add(equipment);

				alreadyProcessedEquipmentIds.Add(equipment.Id);

				equipment = equipmentService.GetXJEquipmentTempById(equipment.PId);
			}
			result.Reverse();
			return result;
		}

		public static IList<XJEquipment4StockIn> GetEquipmentBreadCrumb(this XJEquipment4StockIn equipment,
			IXJEquipmentService equipmentService)
		{
			if (equipment == null)
				throw new ArgumentNullException("equipment");

			var result = new List<XJEquipment4StockIn>();

			//used to prevent circular references
			var alreadyProcessedEquipmentIds = new List<int>();

			while (equipment != null && //not null
				!alreadyProcessedEquipmentIds.Contains(equipment.Id)) //prevent circular references
			{
				result.Add(equipment);

				alreadyProcessedEquipmentIds.Add(equipment.Id);

				var equip = equipmentService.GetXJEquipmentById(equipment.PId);
				if (equip != null)
				{
					equipment = new XJEquipment4StockIn
					{
						Id = equip.Id,
						Name = equip.Name,
						PId = equip.PId,
					};
				}
			}
			result.Reverse();
			return result;
		}

		public static IList<XJEquipment4StockOut> GetEquipmentBreadCrumb(this XJEquipment4StockOut equipment,
			IXJEquipmentService equipmentService)
		{
			if (equipment == null)
				throw new ArgumentNullException("equipment");

			var result = new List<XJEquipment4StockOut>();

			//used to prevent circular references
			var alreadyProcessedEquipmentIds = new List<int>();

			while (equipment != null && //not null
				!alreadyProcessedEquipmentIds.Contains(equipment.Id)) //prevent circular references
			{
				result.Add(equipment);

				alreadyProcessedEquipmentIds.Add(equipment.Id);

				var equip = equipmentService.GetXJEquipmentById(equipment.PId);
				if (equip != null)
				{
					equipment = new XJEquipment4StockOut
					{
						Id = equip.Id,
						Name = equip.Name,
						PId = equip.PId,
					};
				}
			}
			result.Reverse();
			return result;
		}

		public static IList<XJEquipment> GetEquipmentBreadCrumb(this XJEquipment equipment,
			IXJEquipmentService equipmentService,
			bool showHidden = false)
		{
			if (equipment == null)
				throw new ArgumentNullException("equipment");

			var result = new List<XJEquipment>();

			//used to prevent circular references
			var alreadyProcessedEquipmentIds = new List<int>();

			while (equipment != null && //not null
				(showHidden || equipment.Published) && //published
				!alreadyProcessedEquipmentIds.Contains(equipment.Id)) //prevent circular references
			{
				result.Add(equipment);

				alreadyProcessedEquipmentIds.Add(equipment.Id);

				equipment = equipmentService.GetXJEquipmentById(equipment.PId);
			}
			result.Reverse();
			return result;
		}

		/// <summary>
		/// Get category breadcrumb 
		/// </summary>
		/// <param name="category">XJEquipmentCategory</param>
		/// <param name="allCategories">All categories</param>
		/// <param name="aclService">ACL service</param>
		/// <param name="storeMappingService">Store mapping service</param>
		/// <param name="showHidden">A value indicating whether to load hidden records</param>
		/// <returns>XJEquipmentCategory breadcrumb </returns>
		public static IList<XJEquipmentCategory> GetCategoryBreadCrumb(this XJEquipmentCategory category,
			IList<XJEquipmentCategory> allCategories,
			bool showHidden = false)
		{
			if (category == null)
				throw new ArgumentNullException("category");

			var result = new List<XJEquipmentCategory>();

			//used to prevent circular references
			var alreadyProcessedCategoryIds = new List<int>();

			while (category != null && //not null
				!category.Deleted && //not deleted
				(showHidden || category.Published) && //published
				!alreadyProcessedCategoryIds.Contains(category.Id)) //prevent circular references
			{
				result.Add(category);

				alreadyProcessedCategoryIds.Add(category.Id);

				category = (from c in allCategories
							where c.Id == category.PId
							select c).FirstOrDefault();
			}
			result.Reverse();
			return result;
		}
	}
}
